package bll

import (
	"fmt"
	"strconv"
	"time"

	. "7qule.com/sd3/CrossServer/src/bll/player/dal"
	. "7qule.com/sd3/CrossServer/src/bll/player/model"
	"7qule.com/sd3/CrossServer/src/bll/world"
	"7qule.com/sd3/CrossServer/src/common/onlinelog"
	"7qule.com/sd3/CrossServer/src/common/systemConstant"
	"public.com/goutil/stringUtil"
	"public.com/goutil/syncUtil"
)

// 玩家信息务逻辑类
type globalPlayerBll struct {
	// 玩家信息数据
	data map[string]*GlobalPlayer

	// 数据同步对象
	dataLock *syncUtil.RWLocker
}

type GlobalPlayerList []*GlobalPlayer

// --------------------------------初始化--------------------------------------------------

// 玩家信息逻辑类
var GlobalPlayerBll = &globalPlayerBll{
	data:     make(map[string]*GlobalPlayer),
	dataLock: syncUtil.NewRWLocker(),
}

// 初始化
func init() {
	// 注册数据初始化函数
	world.LoadMgr.RegisterFunc(GlobalPlayerBll.StructName(), GlobalPlayerBll.initData)
}

// StructName 模块名
func (this *globalPlayerBll) StructName() string {
	return "GlobalPlayerBll"
}

// initData 初始化数据
// 返回值:
// err:错误信息
func (this *globalPlayerBll) initData() (errList []error) {
	tempList, err := GlobalPlayerDal.GetData()
	if err != nil {
		errList = append(errList, err)
		return
	}

	tempMap := make(map[string]*GlobalPlayer, len(tempList))
	for _, item := range tempList {
		tempMap[item.Id] = item
	}

	if isOk, prevStack, currStack := this.dataLock.Lock(systemConstant.DefaultLockTime); isOk == false {
		errMsg := fmt.Sprintf("Lock timeout! \n上一个堆栈:\n%s  \n当前堆栈:\n%s", prevStack, currStack)
		onlinelog.ErrorLog(errMsg)
		panic(errMsg)
	}
	defer this.dataLock.Unlock()

	this.data = tempMap

	return
}

// --------------------------------生成的基础方法--------------------------------------------------

// GetData 获取数据
// 返回值:
// []*GlobalPlayer:获取到的玩家信息数据
func (this *globalPlayerBll) GetData(isCopy bool) map[string]*GlobalPlayer {
	if isCopy == false {
		return this.data
	}

	if isOk, prevStack, currStack := this.dataLock.RLock(systemConstant.DefaultLockTime); isOk == false {
		errMsg := fmt.Sprintf("Lock timeout! \n上一个堆栈:\n%s  \n当前堆栈:\n%s", prevStack, currStack)
		onlinelog.ErrorLog(errMsg)
		panic(errMsg)
	}
	defer this.dataLock.RUnlock()

	tempMap := make(map[string]*GlobalPlayer, len(this.data))
	for k, v := range this.data {
		tempMap[k] = v
	}

	return tempMap
}

// GetItem 获取数据(内存+数据库)
// 返回值:
// *GlobalPlayer:获取到的玩家信息数据
func (this *globalPlayerBll) GetItem(Id string) (result *GlobalPlayer, exists bool) {
	result, exists = this.getItem2(Id)
	if exists {
		return
	}

	result, err := GlobalPlayerDal.GetPlayerFromDb(Id)
	if err != nil || result == nil {
		return nil, false
	}

	if isOk, prevStack, currStack := this.dataLock.Lock(systemConstant.DefaultLockTime); isOk == false {
		errMsg := fmt.Sprintf("Lock timeout! \n上一个堆栈:\n%s  \n当前堆栈:\n%s", prevStack, currStack)
		onlinelog.ErrorLog(errMsg)
		panic(errMsg)
	}
	defer this.dataLock.Unlock()

	this.data[Id] = result
	exists = true

	return
}

// 获取或初始化数据
// 返回值:
// 		*GlobalPlayer:获取到的玩家信息数据
//		是否是新对象
func (this *globalPlayerBll) getOrInitItem(id string) (*GlobalPlayer, bool) {
	//尝试获取
	result, exists := this.getItem2(id)
	if exists {
		return result, false
	}

	//初始化对象
	result, exists = this.initItem(id)
	if exists {
		return result, false
	}

	return result, true
}

// getItem2 获取数据
// 返回值:
// *GlobalPlayer:获取到的玩家信息数据
func (this *globalPlayerBll) getItem2(id string) (result *GlobalPlayer, exists bool) {
	if isOk, prevStack, currStack := this.dataLock.RLock(systemConstant.DefaultLockTime); isOk == false {
		errMsg := fmt.Sprintf("Lock timeout! \n上一个堆栈:\n%s  \n当前堆栈:\n%s", prevStack, currStack)
		onlinelog.ErrorLog(errMsg)
		panic(errMsg)
	}
	defer this.dataLock.RUnlock()

	result, exists = this.data[id]

	return
}

// 初始化数据
// 返回值:
// 		*GlobalPlayer:获取到的玩家信息数据
//		是否存在
func (this *globalPlayerBll) initItem(id string) (*GlobalPlayer, bool) {
	if isOk, prevStack, currStack := this.dataLock.Lock(systemConstant.DefaultLockTime); isOk == false {
		errMsg := fmt.Sprintf("Lock timeout! \n上一个堆栈:\n%s  \n当前堆栈:\n%s", prevStack, currStack)
		onlinelog.ErrorLog(errMsg)
		panic(errMsg)
	}
	defer this.dataLock.Unlock()

	//写锁中再次判断是否存在
	result, exists := this.data[id]
	if exists {
		return result, true
	}

	//初始化对象
	result = NewGlobalPlayer2()
	result.Id = id

	this.data[id] = result

	return result, false
}

// --------------------------------内部方法--------------------------------------------------

// insert 数据插入
// data:玩家数据
func (this *globalPlayerBll) replace(data *GlobalPlayer) {
	GlobalPlayerDal.Replace(data)
}

// delete 删除数据
// data:玩家数据对象
func (this *globalPlayerBll) delete(id string) {
	GlobalPlayerDal.Delete(id)
}

//获取内存中不活跃的玩家Id列表(1天不活跃)
func (this *globalPlayerBll) getMem1DayNoActivePlayerIds() []string {
	if isOk, prevStack, currStack := this.dataLock.RLock(systemConstant.DefaultLockTime); isOk == false {
		errMsg := fmt.Sprintf("Lock timeout! \n上一个堆栈:\n%s  \n当前堆栈:\n%s", prevStack, currStack)
		onlinelog.ErrorLog(errMsg)
		panic(errMsg)
	}
	defer this.dataLock.RUnlock()

	tempIdList := make([]string, 0)
	second, _ := time.ParseDuration("86400s")
	for _, temp := range this.data {
		if temp.ActiveTime.Add(second).Before(time.Now()) == true {
			tempIdList = append(tempIdList, temp.Id)
		}
	}

	return tempIdList
}

// 获取内存过期玩家id集合(14天不活跃)
func (this *globalPlayerBll) getMem14DayNoActivePlayerIds() []string {
	if isOk, prevStack, currStack := this.dataLock.RLock(systemConstant.DefaultLockTime); isOk == false {
		errMsg := fmt.Sprintf("Lock timeout! \n上一个堆栈:\n%s  \n当前堆栈:\n%s", prevStack, currStack)
		onlinelog.ErrorLog(errMsg)
		panic(errMsg)
	}
	defer this.dataLock.RUnlock()

	//获取需要删除的
	tempList := make([]string, 0)
	for _, temp := range this.data {
		// 14天不活跃的且等级小于70的进行清理
		if temp.ActiveTime.AddDate(0, 0, 14).Before(time.Now()) == true && temp.Lv < 70 {
			tempList = append(tempList, temp.Id)
		}
	}

	return tempList
}

// 清理内存过期数据
func (this *globalPlayerBll) clearMem14DayOverdueData() {
	//获取内存过期玩家id集合
	memOverduePlayerIdList := this.getMem14DayNoActivePlayerIds()
	if len(memOverduePlayerIdList) == 0 {
		return
	}

	if isOk, prevStack, currStack := this.dataLock.Lock(systemConstant.DefaultLockTime); isOk == false {
		errMsg := fmt.Sprintf("Lock timeout! \n上一个堆栈:\n%s  \n当前堆栈:\n%s", prevStack, currStack)
		onlinelog.ErrorLog(errMsg)
		panic(errMsg)
	}
	defer this.dataLock.Unlock()

	// 删除内存数据
	for _, id := range memOverduePlayerIdList {
		delete(this.data, id)
	}
}

// --------------------------------外部方法--------------------------------------------------

// 获取公会玩家数据
func (this *globalPlayerBll) GetGuildMemberList(guildId string) []string {
	result, _ := GlobalPlayerDal.GetGuildMembers(guildId)

	return result
}

// 更新玩家信息数据
func (this *globalPlayerBll) UpdateInfo(id string, serverGroupId, partnerId int32, serverId int32, serverName string, name string, guildId string, guildName string, lv int32, vip int32, fap, maxFap int64, headImageId int32, fashionModelId int32, fashionLevel int32, flag string, logo string, baseInfo, clientPlayerStr string) {
	//获取或初始化对象
	item, isNew := this.getOrInitItem(id)
	oldGuildId := item.GuildId

	//更新数据
	item.Name = name
	item.ServerId = serverId
	item.ServerGroupId = serverGroupId
	item.PartnerId = partnerId
	item.ServerName = serverName
	item.GuildId = guildId
	item.GuildName = guildName
	item.Lv = lv
	item.Vip = vip
	item.Fap = fap
	item.MaxFap = maxFap
	item.HeadImageId = headImageId
	item.FashionModelId = fashionModelId
	item.FashionLevel = fashionLevel
	item.Flag = flag
	item.Logo = logo
	item.BaseInfo = baseInfo
	item.ClientPlayerStr = clientPlayerStr
	item.ActiveTime = time.Now()

	/**
		byron: 因为存在定时清理机制，所以从内存中取数据可能导致内存中不存在，但是数据库中存在的情况。这种情况在dal层使用replace into sql语句解决的
	**/
	if isNew {
		this.replace(item)
	} else {
		this.replace(item)

		//触发玩家公会改变事件
		if oldGuildId != item.GuildId {
			TriggerGuildChangeEvent(item, oldGuildId, item.GuildId)
		}
	}

}

// 清理过期数据(14天未活跃)
func (this *globalPlayerBll) Clear14DayOverdueData() {
	// 清理内存过期数据
	this.clearMem14DayOverdueData()

	//清理db过期数据
	GlobalPlayerDal.ClearDb14DayOverdueData()
}

// 清理内存中不活跃玩家数据，活跃时间在1天之前的定义为不活跃玩家，需要从内存中移除
func (this *globalPlayerBll) ClearInactivePlayerData() {
	//获取内存中不活跃的玩家id列表
	inactivePlayerIdList := this.getMem1DayNoActivePlayerIds()
	if len(inactivePlayerIdList) == 0 {
		return
	}

	if isOk, prevStack, currStack := this.dataLock.Lock(systemConstant.DefaultLockTime); isOk == false {
		errMsg := fmt.Sprintf("Lock timeout! \n上一个堆栈:\n%s  \n当前堆栈:\n%s", prevStack, currStack)
		onlinelog.ErrorLog(errMsg)
		panic(errMsg)
	}
	defer this.dataLock.Unlock()

	// 删除数据
	for _, id := range inactivePlayerIdList {
		delete(this.data, id)
	}
}

// --------------------------------组织客户端数据--------------------------------------------------

// 获取组织数据
func (this *globalPlayerBll) AssembleToClient(player *GlobalPlayer) map[string]interface{} {
	// 对返回结果先赋初值
	resultMap := make(map[string]interface{}, 16)

	resultMap["Id"] = player.Id
	resultMap["PartnerId"] = player.PartnerId
	resultMap["ServerId"] = player.ServerId

	if player.ServerId > 10 {
		strId := strconv.Itoa(int(player.ServerId))
		showId, _ := strconv.Atoi(stringUtil.Substring(strId, 1, len(strId)))

		resultMap["ShowServerId"] = showId
	}

	resultMap["ServerName"] = player.ServerName
	resultMap["Name"] = player.Name
	resultMap["GuildId"] = player.GuildId
	resultMap["GuildName"] = player.GuildName
	resultMap["Lv"] = player.Lv
	resultMap["Vip"] = player.Vip
	resultMap["Fap"] = player.Fap
	resultMap["MaxFap"] = player.MaxFap
	resultMap["HeadImageId"] = player.HeadImageId
	resultMap["FashionModelId"] = player.FashionModelId
	resultMap["FashionLevel"] = player.FashionLevel
	resultMap["Flag"] = player.Flag
	resultMap["Logo"] = player.Logo
	resultMap["BaseInfo"] = player.BaseInfo

	return resultMap
}

// 获取组织数据
func (this *globalPlayerBll) AssembleToClientForNoBaseInfo(player *GlobalPlayer) map[string]interface{} {
	// 对返回结果先赋初值
	resultMap := make(map[string]interface{}, 16)

	resultMap["Id"] = player.Id
	resultMap["PartnerId"] = player.PartnerId
	resultMap["ServerId"] = player.ServerId
	resultMap["ServerName"] = player.ServerName
	resultMap["Name"] = player.Name
	resultMap["GuildId"] = player.GuildId
	resultMap["GuildName"] = player.GuildName
	resultMap["Lv"] = player.Lv
	resultMap["Vip"] = player.Vip
	resultMap["Fap"] = player.Fap
	resultMap["HeadImageId"] = player.HeadImageId
	resultMap["FashionModelId"] = player.FashionModelId
	resultMap["FashionLevel"] = player.FashionLevel
	resultMap["Flag"] = player.Flag
	resultMap["Logo"] = player.Logo
	guild, exists := GlobalGuildBll.GetItem(player.GuildId)
	if exists {
		resultMap["BannerInfo"] = guild.BannerInfo
		resultMap["GuildLv"] = guild.Lv
	} else {
		resultMap["BannerInfo"] = ""
		resultMap["GuildLv"] = 0
	}

	return resultMap
}

//Len()
func (s GlobalPlayerList) Len() int {
	return len(s)
}

//Less():品质将由高到低排序
func (s GlobalPlayerList) Less(i, j int) bool {
	return s[i].Fap > s[j].Fap || s[i].Fap == s[j].Fap && s[i].ActiveTime.After(s[j].ActiveTime)
}

//Swap()
func (s GlobalPlayerList) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}
